package it.antonio.ilpizze.mock;

import it.antonio.ilpizze.aop.Advice;
import it.antonio.ilpizze.aop.Aspect;
import it.antonio.ilpizze.aop.Invocation;
import it.antonio.ilpizze.aop.Pointcut;
import it.antonio.ilpizze.aop.Proxifier;
import it.antonio.ilpizze.mock.expectation.NullExpectation;
import it.antonio.ilpizze.mock.expectation.RealMethodExpectation;

import java.util.HashMap;
import java.util.Map;

public class Mocker extends ArgumentMatchers{

	public static final Expectation<Object> ALWAYS_NULL = new NullExpectation();
	public static final Expectation<Object> CALL_REAL_METHODS = new RealMethodExpectation();

	private static Map<Object, MockHandler> mocks = new HashMap<Object, MockHandler>();

	public static <T> T mock(Class<T> classToMock, Object... args) {
		return mock(ALWAYS_NULL, classToMock, args);
	}

	public static <T> T mock(Expectation<?> defaultExpectation, Class<T> classToMock, Object... args) {
		MockHandler handler = new MockHandler(defaultExpectation);
		MockAspect mockAspect = new MockAspect(handler);
		T instance = Proxifier.withAspects(mockAspect).createInstance(classToMock, args);
		mocks.put(instance, handler);
		return instance;
	}

	public static <T> Stubber<T> when(T notUsed) {
		Invocation invocation = Progress.invocation(); 
		
		MockHandler handler = mocks.get(invocation.getInstance());
		
		return new Stubber<T>(handler);
	}

	public static <T> T verify(Verification verification, T mock) {
		Progress.startVerifiy();
		MockHandler handler = mocks.get(mock);
		handler.verifiy(verification);
		return mock;
	}

	private static class MockAspect extends Aspect {
		private MockHandler mockHandler;

		public MockAspect(MockHandler mockHandler) {
			this.mockHandler = mockHandler;
		}

		@Override
		public Pointcut pointcut() {
			return allMethods();
		}

		@Override
		public Advice advice() {
			return mockHandler;
		}

	}
}
